/*
 * DatabaseAccess.java
 *
 * Copyright(c) 2010
 * Michael Kroez
 * kroez@studiorutton.de
 *
 * All rights reserved.
 *
 * $Id$
 * 
 */

package de.studiorutton.gerengdic.databaseaccess;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

import de.studiorutton.gerengdic.databaseaccess.DatabaseAccessException.ErrorCode;
import de.studiorutton.gerengdic.databaseaccess.records.IndexPositionRecord;
import de.studiorutton.gerengdic.databaseaccess.records.IndexRecord;

public class DatabaseAccess {
	public final static byte PREAMBLE_RECORD_HEADER = 0x01;
	// public static final Charset CHARSET_DEFAULT = Charset.forName("UTF-8");
	public static final String CHARSET_DEFAULT = "UTF-8";
	private String file;
	private RandomAccessFile raf;
	private byte[] buf;
	private ContentAccess ca;

	private IndexPositionRecord lastIPRForward;
	private IndexPositionRecord lastIPRBackward;

	private String preamble;

	public DatabaseAccess(String file) throws DatabaseAccessException {
		try {
			this.raf = new RandomAccessFile(file, "r");
		} catch (FileNotFoundException e) {
			throw new DatabaseAccessException(
					ErrorCode.ERROR_CANNOT_FIND_DICTIONARY);
		}

		readPreamble();
		this.file = file;

		ca = new ContentAccess(this.file, raf);
		ca.readContentRecord();
	}

	public String getPreamble() {
		return preamble;
	}

	public void close() throws DatabaseAccessException {
		try {
			raf.close();
		} catch (IOException e) {
			throw new DatabaseAccessException(e);
		}
		ca.close();
	}

	public ContentAccess getContentAccess() {
		return ca;
	}

	public void readPreamble() throws DatabaseAccessException {
		try {
			raf.seek(0);
			byte header = raf.readByte();
			if (header < 0 || header != PREAMBLE_RECORD_HEADER)
				throw new DatabaseAccessException(
						ErrorCode.ERROR_CANNOT_FIND_PREAMBLE_HEADER);
			int contentSize = raf.readInt();
			buf = new byte[contentSize];
			raf.read(buf, 0, contentSize);
			// System.out.println("readRecord: contentSize = " + contentSize +
			// ".");
			preamble = new String(buf, 0, contentSize, CHARSET_DEFAULT);
			// System.out.println("readRecord:");
			// System.out.println("*" + preamble + "*");
		} catch (IOException e) {
			throw new DatabaseAccessException(
					ErrorCode.ERROR_READING_DICTIONARY, e.getLocalizedMessage());
		}
	}

	public class ViewRecord {
		public final static int CONTENT_KEY = 1;
		public final static int CONTENT_RECORD = 2;
		public String content;
		public int contentType;
		public int index;
	}

	public void setLastIndexPositionRecord(IndexPositionRecord ipr) {
		lastIPRForward = ipr;
		lastIPRBackward = ipr;
	}

	public IndexPositionRecord getLastIPRForward() {
		return lastIPRForward;
	}

	public IndexPositionRecord getLastIPRBackward() {
		return lastIPRBackward;
	}

	public ViewRecord getViewRecordBackward(int viewIndex, int language)
			throws DatabaseAccessException {

		LanguageAccess la = ca.getLanguageAccess(language);
		IndexRecord ir = la.getIndexPositionAccess().getIndexAccess()
				.getIndexRecord(lastIPRBackward.getIndexRecordPosition());
		ViewRecord vr = new ViewRecord();
		if (viewIndex == lastIPRBackward.getViewOffset()) {
			vr.content = ir.getKey();
			vr.contentType = ViewRecord.CONTENT_KEY;
			vr.index = viewIndex;
		} else if (viewIndex > lastIPRBackward.getViewOffset()
				&& viewIndex <= lastIPRBackward.getLastViewOffset()) {
			int recordPosition = ir.getRecordPositions().get(
					lastIPRBackward.getLastViewOffset() - viewIndex);
			vr.content = ca.getRecordAccess().getAtPosition(recordPosition);
			vr.contentType = ViewRecord.CONTENT_RECORD;
			vr.index = viewIndex;
		} else if (viewIndex < lastIPRBackward.getViewOffset()) {
			lastIPRBackward = la.getIndexPositionAccess()
					.getIndexPositionRecordAtIndex(
							lastIPRBackward.getIndex() - 1);
			return getViewRecordBackward(viewIndex, language);
		} else { // ( viewIndex > lastIPRBackward.getLastViewOffset())
			lastIPRBackward = la.getIndexPositionAccess()
					.getIndexPositionRecordAtIndex(
							lastIPRBackward.getIndex() + 1);
			return getViewRecordBackward(viewIndex, language);
		}
		return vr;
	}

	public ViewRecord getViewRecordBackwardIterativ(int viewIndex, int language)
			throws DatabaseAccessException {
		LanguageAccess la = ca.getLanguageAccess(language);

		IndexPositionRecord crrIpr = lastIPRBackward;
		while ( viewIndex >= 0 && (viewIndex < crrIpr.getViewOffset()
				|| viewIndex > crrIpr.getLastViewOffset())) {
			if (viewIndex < crrIpr.getViewOffset()) {
				crrIpr = la.getIndexPositionAccess()
						.getIndexPositionRecordAtIndex(crrIpr.getIndex() - 1);
			} else if (viewIndex > crrIpr.getLastViewOffset())
				crrIpr = la.getIndexPositionAccess()
						.getIndexPositionRecordAtIndex(crrIpr.getIndex() + 1);
		}
		
		if ( viewIndex < 0 )
			return null;
		
		IndexRecord ir = la.getIndexPositionAccess().getIndexAccess()
				.getIndexRecord(crrIpr.getIndexRecordPosition());

		ViewRecord vr = new ViewRecord();
		if (viewIndex == crrIpr.getViewOffset()) {
			vr.content = ir.getKey();
			vr.contentType = ViewRecord.CONTENT_KEY;
			vr.index = viewIndex;
		} else {
			int recordPosition = ir.getRecordPositions().get(
					crrIpr.getLastViewOffset() - viewIndex);
			vr.content = ca.getRecordAccess().getAtPosition(recordPosition);
			vr.contentType = ViewRecord.CONTENT_RECORD;
			vr.index = viewIndex;
		}

		lastIPRBackward = crrIpr;
		return vr;
	}

	public ViewRecord getViewRecordForward(int viewIndex, int language)
			throws DatabaseAccessException {

		LanguageAccess la = ca.getLanguageAccess(language);
		IndexRecord ir = la.getIndexPositionAccess().getIndexAccess()
				.getIndexRecord(lastIPRForward.getIndexRecordPosition());
		ViewRecord vr = new ViewRecord();
		if (viewIndex > la.getIndexPositionAccess().getLastViewOffset())
			return null;
		else if (viewIndex == lastIPRForward.getViewOffset()) {
			vr.content = ir.getKey();
			vr.contentType = ViewRecord.CONTENT_KEY;
			vr.index = viewIndex;
		} else if (viewIndex > lastIPRForward.getViewOffset()
				&& viewIndex <= lastIPRForward.getLastViewOffset()) {
			int recordPosition = ir.getRecordPositions().get(
					lastIPRForward.getLastViewOffset() - viewIndex);
			vr.content = ca.getRecordAccess().getAtPosition(recordPosition);
			vr.contentType = ViewRecord.CONTENT_RECORD;
			vr.index = viewIndex;
		} else if (viewIndex < lastIPRForward.getViewOffset()) {
			lastIPRForward = la.getIndexPositionAccess()
					.getIndexPositionRecordAtIndex(
							lastIPRForward.getIndex() - 1);
			return getViewRecordForward(viewIndex, language);
		} else { // ( viewIndex > lastIPRForward.getLastViewOffset())
			lastIPRForward = la.getIndexPositionAccess()
					.getIndexPositionRecordAtIndex(
							lastIPRForward.getIndex() + 1);
			return getViewRecordForward(viewIndex, language);
		}
		return vr;
	}

	public ViewRecord getViewRecordForwardIterativ(int viewIndex, int language)
			throws DatabaseAccessException {
		LanguageAccess la = ca.getLanguageAccess(language);

		IndexPositionRecord crrIpr = lastIPRForward;
		while (viewIndex <=  la.getIndexPositionAccess().getLastViewOffset() && (
				viewIndex < crrIpr.getViewOffset()
				|| viewIndex > crrIpr.getLastViewOffset())) {
			if (viewIndex < crrIpr.getViewOffset()) {
				crrIpr = la.getIndexPositionAccess()
						.getIndexPositionRecordAtIndex(crrIpr.getIndex() - 1);
			} else if (viewIndex > crrIpr.getLastViewOffset())
				crrIpr = la.getIndexPositionAccess()
						.getIndexPositionRecordAtIndex(crrIpr.getIndex() + 1);
		}
		
		if ( viewIndex > la.getIndexPositionAccess().getLastViewOffset())
			return null;
		
		IndexRecord ir = la.getIndexPositionAccess().getIndexAccess()
				.getIndexRecord(crrIpr.getIndexRecordPosition());

		ViewRecord vr = new ViewRecord();
		if (viewIndex == crrIpr.getViewOffset()) {
			vr.content = ir.getKey();
			vr.contentType = ViewRecord.CONTENT_KEY;
			vr.index = viewIndex;
		} else {
			int recordPosition = ir.getRecordPositions().get(
					crrIpr.getLastViewOffset() - viewIndex);
			vr.content = ca.getRecordAccess().getAtPosition(recordPosition);
			vr.contentType = ViewRecord.CONTENT_RECORD;
			vr.index = viewIndex;
		}

		lastIPRForward = crrIpr;
		return vr;
	}

	// public int getViewRecordType( int viewIndex, int language, boolean
	// withOffset ) throws DatabaseAccessException {
	// if ( withOffset ) {
	// viewIndex = lastIndexPositionRecordViewType.getViewOffset() + viewIndex;
	// }
	//
	// LanguageAccess la = ca.getLanguageAccess(language);
	// int contentType;
	// if ( viewIndex == lastIndexPositionRecordViewType.getViewOffset()) {
	// contentType = ViewRecord.CONTENT_KEY;
	// } else if ( viewIndex > lastIndexPositionRecordViewType.getViewOffset()
	// &&
	// viewIndex <= lastIndexPositionRecord.getLastViewOffset() ) {
	// contentType = ViewRecord.CONTENT_RECORD;
	// } else if ( viewIndex < lastIndexPositionRecordViewType.getViewOffset())
	// {
	// lastIndexPositionRecordViewType =
	// la.getIndexPositionAccess().getIndexPositionRecordAtIndex(
	// lastIndexPositionRecordViewType.getIndex() - 1);
	// return getViewRecordType( viewIndex, language, false );
	// } else { // ( viewIndex > lastIndexPositionRecord.getLastViewOffset())
	// lastIndexPositionRecordViewType =
	// la.getIndexPositionAccess().getIndexPositionRecordAtIndex(
	// lastIndexPositionRecordViewType.getIndex() + 1 );
	// return getViewRecordType( viewIndex, language, false );
	// }
	// return contentType;
	// }

	public int getViewIndexSize(int language) {
		return ca.getLanguageAccess(language).getIndexPositionAccess()
				.getLastViewOffset();
	}

}
